package org.xteam.sled.semantic.exp;

import java.util.Stack;

public class ExpRewriter extends DefaultExpVisitor implements IExpRewriter {

	protected Stack<Exp> stack = new Stack<Exp>();
	
	public Exp rewrite(Exp e) {
		e.visit(this);
		return stack.pop();
	}
	
	@Override
	public void visitVar(ExpVar expVar) {
		stack.push(expVar);
	}
	
	@Override
	public void visitConst(ExpConst expConst) {
		stack.push(expConst);
	}

	@Override
	public void visitPC(ExpPC expPC) {
		stack.push(expPC);
	}

	public void visitWiden(ExpWiden expWiden) {
		expWiden.getExpr().visit(this);
		stack.push(new ExpWiden(stack.pop(), expWiden.width()));
	}
	
	public void visitDiv(ExpDiv expDiv) {
		expDiv.getExpr().visit(this);
		stack.push(new ExpDiv(expDiv.factor(), stack.pop()));
	}
	
	public void visitMod(ExpMod expMod) {
		expMod.getExpr().visit(this);
		stack.push(new ExpMod(expMod.factor(), stack.pop()));
	}
	
	@Override
	public void visitAdd(ExpAdd expAdd) {
		expAdd.left().visit(this);
		Exp l = stack.pop();
		expAdd.right().visit(this);
		stack.push(new ExpAdd(l, stack.pop()));
	}
	
	@Override
	public void visitSub(ExpSub expSub) {
		expSub.left().visit(this);
		Exp l = stack.pop();
		expSub.right().visit(this);
		stack.push(new ExpSub(l, stack.pop()));
	}

	@Override
	public void visitCondition(ExpCondition expCondition) {
		expCondition.left().visit(this);
		Exp l = stack.pop();
		expCondition.right().visit(this);
		stack.push(new ExpCondition(expCondition.condition(), l, stack.pop()));
	}

	@Override
	public void visitMul(ExpMul expMul) {
		expMul.getExpr().visit(this);
		stack.push(new ExpMul(expMul.factor(), stack.pop()));
	}
	
	@Override
	public void visitSlice(ExpSlice expSlice) {
		expSlice.getExpr().visit(this);
		stack.push(new ExpSlice(stack.pop(), expSlice.low(), expSlice.width()));
	}
	
	@Override
	public void visitNarrows(ExpNarrows expNarrows) {
		expNarrows.getExpr().visit(this);
		stack.push(new ExpNarrows(stack.pop(), expNarrows.width()));
	}
	
	@Override
	public void visitNarrowu(ExpNarrowu expNarrowu) {
		expNarrowu.getExpr().visit(this);
		stack.push(new ExpNarrowu(stack.pop(), expNarrowu.width()));
	}
	
	@Override
	public void visitForce(ExpForce expForce) {
		expForce.getExpr().visit(this);
		stack.push(new ExpForce(stack.pop()));
	}
	
	@Override
	public void visitShift(ExpShift expShift) {
		expShift.getExpr().visit(this);
		stack.push(new ExpShift(stack.pop(), expShift.amount()));
	}
	
	@Override
	public void visitInstanceTagged(ExpInstanceTagged expInstanceTagged) {
		expInstanceTagged.getExpr().visit(this);
		stack.push(new ExpInstanceTagged(stack.pop(), expInstanceTagged.getConstructor(), expInstanceTagged.getUid()));
	}

	@Override
	public void visitInstanceInput(ExpInstanceInput expInstanceInput) {
		expInstanceInput.getExpr().visit(this);
		stack.push(new ExpInstanceInput(stack.pop(), expInstanceInput.getConstructor(), expInstanceInput.getOperand()));
	}
	
	@Override
	public void visitBindingInstance(ExpBindingInstance expBindingInstance) {
		stack.push(expBindingInstance);
	}
}
